package com.origin.chaos.framework.core;

import com.origin.chaos.common.util.ObjectUtil;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Redis画布，所有像素信息以List的方式存放在Redis中，例如宽为a，高为b的画布，会在redis中存在b个List，每个List长度为a
 * todo: 可以考虑尝试实现一个数组服务器，类似Redis，提供高效的数组读写操作
 *
 * @version 1.0
 * @author: tao.ding
 * @date 2021-07-15
 */
public class RedisPixelCanvas extends AbstractPixelCanvas implements PixelCanvas {

    /**
     * 单个画布的配置信息hash键前缀，用来记录单个画布的配置信息
     */
    public static final String CANVAS_HASH_KEY_PREFIX = "canvas:";

    /**
     * redis缓存属性key
     */
    public static final String NAME_KEY = "name",
            WIDTH_KEY = "width",
            HEIGHT_KEY = "height",
            PAINT_INTERVAL_TIME_KEY = "paintIntervalTime",
            TOTAL_PAINTED_POINTS_KEY = "totalPaintedPoints";

    /**
     * 哪些用户加入了这个画布的集合key
     */
    public static final String JOINED_USER_SET_KEY = "joined_user:";

    /**
     * 获取redis中存放画布信息的hash键名
     *
     * @param id 画布ID
     * @return
     */
    public static String canvasHashKey(long id) {
        return CANVAS_HASH_KEY_PREFIX + id;
    }

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 存放画布配置信息的hash键名称
     */
    private String redisHashKey;

    public RedisPixelCanvas(Long id, String name, int width, int height, int paintIntervalTime, StringRedisTemplate stringRedisTemplate) {
        super(id, name, width, height, paintIntervalTime);
        this.stringRedisTemplate = stringRedisTemplate;
        this.redisHashKey = canvasHashKey(id);
    }

    /**
     * 获取当前画布第hPos行在redis的List key
     *
     * @param hPos y坐标
     * @return List key
     */
    private String getRedisListKey(int hPos) {
        return "canvas:" + this.getId() + ":" + hPos;
    }

    @Override
    public void updateName(String name) {
        super.updateName(name);
        this.stringRedisTemplate.opsForHash().put(this.redisHashKey, NAME_KEY, name);
    }

    @Override
    public void updatePaintIntervalTime(int paintIntervalTime) {
        super.updatePaintIntervalTime(paintIntervalTime);
    }

    @Override
    public long getTotalPaintedPoints() {
        String val = Optional.ofNullable(this.stringRedisTemplate.opsForHash().get(this.redisHashKey, TOTAL_PAINTED_POINTS_KEY)).orElse("0").toString();
        return Long.parseLong(val);
    }

    @Override
    public void incrPaintedPoint(long var) {
        this.stringRedisTemplate.opsForHash().increment(this.redisHashKey, TOTAL_PAINTED_POINTS_KEY, var);
    }

    @Override
    public long getTotalJoinedUser() {
        return ObjectUtil.ofLong(this.stringRedisTemplate.opsForSet().size(JOINED_USER_SET_KEY + this.getId()));
    }

    /**
     * 将用户加入到画布中
     *
     * @param userId 用户ID
     */
    @Override
    public void userJoin(long userId) {
        this.stringRedisTemplate.opsForSet().add(JOINED_USER_SET_KEY + this.getId(), userId + "");
    }

    @Override
    public void update(int hPos, int wPos, int colorRgbInt) {
        // 本地缓存更新
        super.update(hPos, wPos, colorRgbInt);
        // Redis数据更新
        this.stringRedisTemplate.opsForList().set(this.getRedisListKey(hPos), wPos, colorRgbInt + "");
    }


    /**
     * 根据画布ID，从Redis中加载本地缓存，初始化本地画布;如果Redis中还没有该画布，则初始化并设置为纯白画布
     *
     * @param ifNewCreation true:新键画布，初始化为纯白 false:从redis中加载到本地缓存
     */
    @Override
    public void init(boolean ifNewCreation) {
        if (ifNewCreation) {
            this.initWithColor(Color.WHITE);
        } else {
            this.initFromRedis();
        }
    }

    /**
     * 从redis中加载画布数据到本地缓存
     */
    private void initFromRedis() {
        for (int i = 0; i < this.getHeight(); i++) {
            final String rowKey = this.getRedisListKey(i);
            List<String> rowValue = this.stringRedisTemplate.opsForList().range(rowKey, 0, -1);
            if (null != rowValue && rowValue.size() == this.getWidth()) {
                for (int j = 0; j < rowValue.size(); j++) {
                    this.getRgbIntArray()[i][j] = Integer.parseInt(rowValue.get(j));
                }
            }
        }
    }

    @Override
    public void initWithColor(Color color) {
        // 本地缓存数据初始化
        super.initWithColor(color);
        // Redis数据初始化
        String rgbIntStr = color.getRGB() + "";
        List<String> rgbStrList = Stream.generate(() -> rgbIntStr).limit(this.getWidth()).collect(Collectors.toList());
        for (int i = 0; i < this.getHeight(); i++) {
            final String rowKey = this.getRedisListKey(i);
            this.stringRedisTemplate.delete(rowKey);
            this.stringRedisTemplate.opsForList().leftPushAll(rowKey, rgbStrList);
        }
    }

    @Override
    public void flush() throws IOException {

    }
}
